﻿using Devil;
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace GameToolkit
{
    public class SortedList<T> : ICollection<T>
    {
        private int mLen;
        private T[] mData;
        private bool mIsStruct;
        public bool ReplaceMode { get; set; }

        GetterDelegate<T, int> mIdentifier;

        public int Count { get { return mLen; } }

        public T this[int index] { get { return mData[index]; } }

        public bool IsReadOnly { get { return false; } }

        public SortedList(int initCapacity, GetterDelegate<T, int> identifier, bool alwaysAsValueType = false)
        {
            if (identifier == null)
                mIdentifier = Identify;
            else
                mIdentifier = identifier;
            mData = new T[initCapacity];
            mIsStruct = alwaysAsValueType || typeof(T).IsValueType;
        }

        public void CopyTo(T[] arr, int index)
        {
            var len = Mathf.Min(arr.Length - index, mLen);
            for (int i = 0; i < len; i++)
            {
                arr[i + index] = mData[i];
            }
        }

        int Identify(T v)
        {
            return v == null ? 0 : v.GetHashCode();
        }

        int BinsearchFromRightIndex(int id, out bool replace)
        {
            int l = 0;
            int r = mLen - 1;
            int c;
            int cs;
            int ret = -1;
            while (l <= r)
            {
                c = (l + r) >> 1;
                cs = mIdentifier(mData[c]);// compareResult(list[c]);
                if (cs == id)
                {
                    replace = true;
                    return c;
                }
                else if (cs > id)
                {
                    r = c - 1;
                    ret = c;
                }
                else
                {
                    l = c + 1;
                }
            }
            replace = false;
            return ret;
        }

        void EnsureSize(int size)
        {
            if (mData.Length < size)
            {
                var dt = new T[Mathf.Max(mData.Length << 1, size)];
                if (mLen > 0)
                    Array.Copy(mData, dt, mLen);
                mData = dt;
            }
        }

        public void Add(T value)
        {
            Add(value, ReplaceMode);
        }

        public bool Add(T value, bool alwaysReaplace)
        {
            var id = mIdentifier(value);
            bool replace;
            var index = BinsearchFromRightIndex(id, out replace);
            EnsureSize(mLen + 1);
            if (index == -1)
            {
                mData[mLen++] = value;
                return true;
            }
            else if (replace)
            {
                if (alwaysReaplace)
                    mData[index] = value;
                return alwaysReaplace;
            }
            else
            {
                mLen++;
                for (int i = mLen - 1; i > index; i--)
                {
                    mData[i] = mData[i - 1];
                }
                mData[index] = value;
                return true;
            }
        }

        public T GetValue(int identify)
        {
            var i = IndexOf(identify);
            if (i == -1)
                return default;
            else
                return mData[i];
        }

        public void RemoveAt(int index)
        {
            mLen--;
            for (int i = index; i < mLen; i++)
            {
                mData[i] = mData[i + 1];
            }
            mData[mLen] = default;
        }

        public T RemoveValue(int id)
        {
            var i = IndexOf(id);
            if (i == -1)
                return default;
            var v = mData[i];
            RemoveAt(i);
            return v;
        }

        public void Clear()
        {
            if (!mIsStruct)
            {
                for (int i = 0; i < mLen; i++)
                    mData[i] = default;
            }
            mLen = 0;
        }

        public bool Remove(T value)
        {
            var i = IndexOf(mIdentifier(value));
            if (i != -1)
            {
                RemoveAt(i);
                return true;
            }
            else
            {
                return false;
            }
        }

        public int IndexOf(int id)
        {
            int l = 0;
            int r = mLen - 1;
            int c = l;
            int cs;
            while (l <= r)
            {
                c = (l + r) >> 1;
                cs = mIdentifier(mData[c]);// compareResult(list[c]);
                if (cs == id)
                {
                    return c;
                }
                else if (cs > id)
                {
                    r = c - 1;
                }
                else
                {
                    l = c + 1;
                }
            }
            return -1;
        }

        public bool Contains(T value)
        {
            return IndexOf(mIdentifier(value)) != -1;
        }

        public IEnumerator<T> GetEnumerator()
        {
            return new SliceEnumerator<T>(mData, 0, mLen);
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return new SliceEnumerator<T>(mData, 0, mLen);
        }
    }
}